C.S.M.P. Digest Wed, 10 Feb 93 Volume 2 : Issue 11 Today's Topics: Help for a new programmer (CDEF's & Debuging Resource Code) Proper way to do Self-Modifying code? help- sound playing w/ bufferCmd code for Finding Prefs File (long) The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly. The digest is a collection of article threads from the internet newsgroup comp.sys.mac.programmer. It is designed for people who read c.s.m.p. semi- regularly and want an archive of the discussions. If you don't know what a newsgroup is, you probably don't have access to it. Ask your systems administrator(s) for details. If you don't have access to news, you can post articles to any newsgroup by mailing your article to newsgroup@cs.utexas.edu So, to post an article to comp.sys.mac.programmer, mail your article to comp-sys-mac-programmer@cs.utexas.edu Note the '-' instead of '.' in the newsgroup name. Be sure to ask that replies be emailed to you instead of posted to the group, and give your email address. Each issue of the digest contains one or more sets of articles (called threads), with each set corresponding to a 'discussion' of a particular subject. The articles are not edited; all articles included in this digest are in their original posted form (as received by our news server at cs.uoregon.edu). Article threads are not added to the digest until the last article added to the thread is at least one month old (this is to ensure that the thread is dead before adding it to the digest). Article threads that consist of only one message are generally not included in the digest. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu [128.223.8.8] in the directory /pub/mac/csmp-digest. Be sure to read the file /pub/mac/csmp-digest/README before downloading any files. The most recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the directory /info-mac/digest/csmp. If you don't have ftp capability, the sumex archive has a mail server; send a message with the text '$MACarch help' (no quotes) to LISTSERV@ricevm1.rice.edu for more information. The digest is also available via email. Just send a note saying that you want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will automatically receive each new issue as it is created. Sorry, back issues are not available through the mailing list. Send administrative mail to mkelly@cs.uoregon.edu. ------------------------------------------------------- From: kevinb@engr.LaTech.edu (Kevin P. Brunner) Subject: Help for a new programmer (CDEF's & Debuging Resource Code) Date: 6 Jan 93 04:55:39 GMT Organization: Louisiana Tech University I've been working on the mac for the last 6 months, recently converted from ibm p.c. I'm getting my feet wet with controls and resources. I have been able to create small apps with standard mac stuff, but would like to design my own controls. Inside Macintosh does a lot for stating generalities... "you need this, and one of these, and, oh!, one of these too..." but no specific examples in this case. So I've tried creating a CDEF which responds only to the draw message and returns zero (0) for everything else. It responds, but draws something completely different than what the code says. The code inverts a block of pixels in a window (it works when called from the "main" block of code, but not when the resource is compiled seperately -- I get a rounded box!) Has anyone written custom controls? If so, do you mind sharing a portion of your code and development technique? Also, any help on using Think C to debug resources would be appreciated. I have most of the Inside Mac's and both Programming the Macintosh's (vol 1 and 2) I have looked elsewhere but have found nothing with coding examples or techniques. Book or Article references would be appreciated as well. Thanks in advance! kevinb@engr.latech.edu +++++++++++++++++++++++++++ Organization: Royal Institute of Technology, Stockholm, Sweden Date: Wed, 6 Jan 1993 14:56:05 GMT > How to debug CDEF in Think C Well, you could try and create a CDEF that's only 6 bytes, load it in, make it contain a JMP instruction and the address of a function in your application, and flush the cache (after locking the resource) Something like: typedef struct cdef { unsigned short jmp ; short * ( addr ) ( ) ; } cdefStub ; pascal short MyRealCDEF ( whatever ) { Do the stuff } /* DO this before creating any controls */ cdefStub stub ; Handle h = GetReosurce ( 'CDEF' , 4711 ) ; stub . jmp = 0x4000 ; /* Have to look this up, JMP Immediate */ stub . addr = MyRealCDEF ; HLockHi ( h ) ; /* Top of heap to avoid fragmentation */ * ( cdefStub * ) * h = stub ; if ( TrapAvailable ( _HWTrap ) ) { FlushInstructionCache ( ) ; } Now anything going to CDEF 4711 in your program will go to the function MyRealCDEF, and you can use the source debugger on it, and reference globals even! Cool! - -- -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe -- Cookie Jar: Vanilla Yoghurt with Crushed Oreos. --------------------------- From: at15+@andrew.cmu.edu (Andrew Lewis Tepper) Subject: Proper way to do Self-Modifying code? Date: 3 Jan 93 10:55:11 GMT Organization: School of Computer Science, Carnegie Mellon, Pittsburgh, PA I'm writing a BitBlt routine that must be _very_ fast. I want to do animation of many large 256 color objects of a game. It seems the quickest way to do this would be to take an object before the animation starts, and create a sequence of move instructions that would write to just those addresses I want. To draw the object I would just load a register with the apropriate starting address and call it's draw routine. Is there an accepted way to do this on the Mac? I know that self-modifying code can cause problems on chips with caches, so I want to be as "kosher" as possible. Thanks, Andy +++++++++++++++++++++++++++ Date: 4 Jan 93 12:03:53 GMT Organization: Royal Institute of Technology, Stockholm, Sweden In at15+@andrew.cmu.edu (Andrew Lewis Tepper) writes: >Is there an accepted way to do this on the Mac? I know that >self-modifying code can cause problems on chips with caches, so I want >to be as "kosher" as possible. if ( TrapAvailable ( _HwPriv ) ) { /* FIC also calls FlushDataCache */ FlushInstructionCache ( ) ; } Of course you'll want to put the result of TrapAvailable in a global at startup. Not to mention first modifying ALL the figures, then flushing the cache ONCE before starting to call the code. However, I think you could write the code quite conflict-free and as fast using just address-register-relative addressing and maybe a data register for rowBytes. Modifying the instructions data takes time to do; I think using a register pair would be FASTER - and more compatible. Cheers, / h+ - -- -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe -- -- I don't fear death, it's dying that scares me. +++++++++++++++++++++++++++ From: alexr@apple.com (Alexander M. Rosenberg) Date: 5 Jan 93 18:33:37 GMT Organization: Hackers Anonymous In article <1993Jan4.120353.16699@kth.se> Jon Wltte, d88-jwa@dront.nada.kth.se writes: > However, I think you could write the code quite conflict-free > and as fast using just address-register-relative addressing > and maybe a data register for rowBytes. Modifying the instructions > data takes time to do; I think using a register pair would be > FASTER - and more compatible. Immediate moved bitmaps are _much_ faster than those drawn via table lookups. This is a technique that they call "Compiled Bitmaps" in rec.games.programmer. Immediate move instructions are generally faster than using an address register. It adds up, especially when lots of small bitmaps may be necessary. The one shot compile may even be possible to do at compile time, depending on the way the screen bitmap is available on the target machine. The problem with compiled bitmaps is that they typically are double in size over the plan data, which is why Andy would want to compile them when the game is launched. Several games take a long time to start up, like Maelstrom (although this is probably because Maelstrom keeps all it's sprite data as icl8 resources, and loading, locking, and detaching that many resources takes time). - --------------------------------------------------------------------------- - - Alexander M. Rosenberg - INTERNET: alexr@apple.com - Yoyodyne - - - 330 Waverley St., Apt B - UUCP:ucbvax!apple!alexr - Propulsion - - - Palo Alto, CA 94301 - - Systems - - - (415) 329-8463 - Nobody is my employer so - :-) - - - (408) 974-3110 - nobody cares what I say. - - +++++++++++++++++++++++++++ Date: 5 Jan 93 20:25:43 GMT Organization: Royal Institute of Technology, Stockholm, Sweden In <1993Jan5.183337.20064@gallant.apple.com> alexr@apple.com (Alexander M. Rosenberg) writes: >Immediate move instructions are generally faster than using an address >register. It adds up, especially when lots of small bitmaps may be necessary. Hmm, well, I'm not convinced. Let's say you move a bitmap. Using an address register, you will only have to change the register. For a "compiled" bitmap, you would have to change each and every immediate move instruction. Not to mention that immediate instructions and longer, and thus takes more bus cycles in fetch; those bus cycles do also compete with the cycles spent writing to VRAM. (Some slow NuBus cards take 7 cycles for a longword write, but then fetching an 8-byte instruction instead of a 4-byte one takes 4 extra cycles on an LC) It all comes out in amortized performance; if the blitting has to be raving fast, but you have ample of time to precompute the next image, compiled bitmaps would be OK (except they flush the cache on 040s; that's bad speed-wise) but if you have general throughput problems, register-relative blitting is DEFINATELY the right thing to do. Just pass rowBytes in d0 and the address of the first pixel in a0, and make VERY SURE that address is longword aligned or you will suffer much more from misaligned writes than you will from the difference in register-based and immediate addressing. >The one shot compile may even be possible to do at compile time, depending on >the way the screen bitmap is available on the target machine. The problem with Not on the Mac, of course. However, certain other systems have a measly register set and the screen card (note the singularis) on a fixed address; I'm sure the timings you cite are for VGA and not for anything flexible like the mac QD device model. Cheers, / h+ - -- -- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe -- This signature is kept shorter than 4 lines in the interests of UseNet S/N ratio. +++++++++++++++++++++++++++ From: kent@lloyd.Camex.COM (Kent Borg) Date: 5 Jan 93 21:31:38 GMT Organization: Camex Inc., Boston MA In article <1993Jan4.120353.16699@kth.se> d88-jwa@dront.nada.kth.se (Jon Wdtte) writes: >In at15+@andrew.cmu.edu (Andrew Lewis Tepper) writes: > >>Is there an accepted way to do this on the Mac? I know that >>self-modifying code can cause problems on chips with caches, so I want >>to be as "kosher" as possible. I hate being non-Kosher, but just today some of my code was modifying itself. (How can a proc in the sleep queue find my A5 world anyway?) My "to do" list said I should check before I trap, so I added: > if ( TrapAvailable ( _HwPriv ) ) { > > /* FIC also calls FlushDataCache */ > FlushInstructionCache ( ) ; > } It didn't work. At least with my .h-files, the spelling is "HWTrap". (And now I need to type in the source for TrapAvailable()--now I remember why I didn't do this before.) Yes, trivial, so instead consider my post as follows: How should one find one's A5 world from within a sleep queue procedure? - -- Kent Borg kent@camex.com or kentborg@aol.com (when it is *working*) H:(617) 776-6899 W:(617) 426-3577 As always, things look better when some costs are left out. -Economist 3-28-92 p. 94 +++++++++++++++++++++++++++ From: neeri@iis.ethz.ch (Matthias Neeracher) Date: 7 Jan 93 22:23:29 GMT Organization: Integrated Systems Laboratory, ETH, Zurich In article <1993Jan05.163138.20910@lloyd.Camex.COM>, kent@lloyd.Camex.COM (Kent Borg) writes: > Yes, trivial, so instead consider my post as follows: How should one > find one's A5 world from within a sleep queue procedure? I have never written a sleep queue proc, but wouldn't the following work ? TYPE TheBigSleep = RECORD sleepy : SleepQRec; wonderfulWorld: A5WorldPtr; /* Whatever you want */ END; ... valium : TheBigSleep; ... SleepQInstall(@valium); According to TFM, your sleep routine will be called with a pointer to valium in A0 that it can cast to the appropriate type. Am I missing anything ? Matthias - ----- Matthias Neeracher neeri@iis.ethz.ch "I wouldn't recommend sex, drugs or insanity for everyone, but they've always worked for me." -- Hunter S. Thompson +++++++++++++++++++++++++++ From: kent@lloyd.Camex.COM (Kent Borg) Date: 8 Jan 93 17:50:14 GMT Organization: Camex Inc., Boston MA In article neeri@iis.ethz.ch (Matthias Neeracher) writes: >In article <1993Jan05.163138.20910@lloyd.Camex.COM>, kent@lloyd.Camex.COM (Kent Borg) writes: >> Yes, trivial, so instead consider my post as follows: How should one >> find one's A5 world from within a sleep queue procedure? > >I have never written a sleep queue proc, but wouldn't the following work ? > >TYPE TheBigSleep = > RECORD > sleepy : SleepQRec; > wonderfulWorld: A5WorldPtr; /* Whatever you want */ > END; >... >valium : TheBigSleep; >... >SleepQInstall(@valium); > >According to TFM, your sleep routine will be called with a pointer to >valium in A0 that it can cast to the appropriate type. Am I missing anything ? No, you are not missing anything, I was the one missing. Sure looks like that will work. Um, though a healthy dose of "RTFM" is appropiate here, I think there is also a little of another effect: "It is always in the *first* place you look, you just weren't looking carefully enough when you looked there because you didn't yet know how lost it was." I spotted the A0 note, realized that it was not a pointer to my A5, nor was it a refcon, so I looked further. By the time I realized that there was no A5 or refcon sitting waiting for me I had forgotten that I might use A0 to *find* one. Stupid I am sometimes. Thanks for the help. - -- Kent Borg kent@camex.com or kentborg@aol.com (when it is *working*) H:(617) 776-6899 W:(617) 426-3577 As always, things look better when some costs are left out. -Economist 3-28-92 p. 94 --------------------------- From: tmorrow@oracle.com (Thomas Morrow) Subject: help- sound playing w/ bufferCmd Date: 2 Jan 93 23:19:12 GMT Organization: Oracle Corporation, Belmont, CA I am having trouble using SndDoCommand to play a sampled sound header. I do: SndCommand mySndCmd; /* mySndH is a handle to a sound header and data */ /* mySndChan is already properly opened */ mySndCmd.cmd; mySndCmd.param1 = 0; mySndCmd.param2 = (long)(*mySndH) & 0xffffff; SndDoCommand(mySndChan, mySndCmd, false); This does not work for some reason; I can't figure out why because if I replace the SndDoCommand with a Snd Play, it works: SndPlay(mySndChan, mySndH, true); So the channel and header must be alright. I am new to Mac programming, and wasn't sure exactly how to set param2 (a long) to the header address (Ptr)... The IM IV uses ORD4() to do the type conversion, but I am using Think C rather than Lisa Pascal. My way seems to work, but what is the elegant way? Anyway, I use SndChannelStatus to get the status every event loop cycle, and the SCStatus returned is blank (zero) in all except CPUload (24), ChannelAttributes (initMono), and ChannelBusy (true). How can the channel be busy if StartTime == endTime == 0? Please email responses. I will summarize. Tom tmorrow@us.oracle.com - -- - --------------------------------------------------------------------------- Tom Morrow 3OP4 voice:415-506-2202 Office: 427 tmorrow@us.oracle.com fax:415-506-7292 Application Object Library - --------------------------------------------------------------------------- +++++++++++++++++++++++++++ From: REEKES@applelink.apple.com (Jim Reekes) Date: Wed, 6 Jan 1993 00:36:46 GMT Organization: Apple Computer, Inc. In article , tmorrow@oracle.com (Thomas Morrow) wrote: > > > I am having trouble using SndDoCommand to play a sampled sound header. > > I do: > SndCommand mySndCmd; > > /* mySndH is a handle to a sound header and data */ > /* mySndChan is already properly opened */ > mySndCmd.cmd; > mySndCmd.param1 = 0; > mySndCmd.param2 = (long)(*mySndH) & 0xffffff; > SndDoCommand(mySndChan, mySndCmd, false); > > This does not work for some reason; I can't figure out why because if > I replace the SndDoCommand with a Snd Play, it works: > > SndPlay(mySndChan, mySndH, true); > > So the channel and header must be alright. I am new to Mac > programming, and wasn't sure exactly how to set param2 (a long) to the > header address (Ptr)... The IM IV uses ORD4() to do the type > conversion, but I am using Think C rather than Lisa Pascal. My way > seems to work, but what is the elegant way? > > Anyway, I use SndChannelStatus to get the status every event loop > cycle, and the SCStatus returned is blank (zero) in all except CPUload > (24), ChannelAttributes (initMono), and ChannelBusy (true). How can > the channel be busy if StartTime == endTime == 0? You cannot use the 'snd ' resource for the bufferCmd. You must point to the SoundHeader within the resource. Also, the code above didn't set the cmd to bufferCmd. Here's some code anyone can use to find the sound header in a 'snd ' resource. Then change your code above into... mySndCmd.cmd = bufferCmd; mySndCmd.param1 = 0; mySndCmd.param2 = (long) *mySndH + offset; err = SndDoCommand(mySndChan, &mySndCmd, true); // find the sound header inside of the given snd handle OSErr GetBufferOffset(Handle sndHandle, long *offset) { short howManyCmds; Ptr cruisePtr; OSErr result; if (sndHandle == nil) return nilHandleErr; if (*sndHandle == nil) return nilHandleErr; result = noErr; *offset = 0; // set the pointer past the first two words of the snd // this is correct for both format 1 and 2 resources cruisePtr = *sndHandle + offsetof(SndListResource, modifierPart); // if it's a format 1, then point past the modifier parts if ( ((SndListPtr)*sndHandle)->format == firstSoundFormat ) cruisePtr += sizeof(ModRef) * ((SndListPtr)*sndHandle)->numModifiers; // now pointing at number of cmds howManyCmds = *((short *)cruisePtr); cruisePtr += sizeof(howManyCmds); // cruisePtr is now at the first sound command // cruise all commands and find a soundCmd or bufferCmd do { switch (((SndCmdPtr)cruisePtr)->cmd) { case (soundCmd | dataOffsetFlag): case (bufferCmd | dataOffsetFlag): *offset = ((SndCmdPtr)cruisePtr)->param2; howManyCmds = 0; // done, get out of loop break; default: // catch any other type of cmd cruisePtr += sizeof(SndCommand); howManyCmds -= 1; break; } } while (howManyCmds >= 1); // done with all the commands if (*offset == 0) // never found sound header return badFormat; return(result); } - ----------------------------------------------------------------------- Jim Reekes, Polterzeitgeist | Macintosh Toolbox Engineering | Sound Manager Expert Apple Computer, Inc. | "All opinions expressed are mine, and do 20525 Mariani Ave. MS: 81-KS | not necessarily represent those of my Cupertino, CA 95014 | employer, Apple Computer Inc." --------------------------- From: Sproul@sproul.sproul.com (Mark Sproul) Subject: code for Finding Prefs File (long) Date: 5 Jan 93 18:15:16 GMT Organization: Sproul Consulting Below is code for finding and working with prefs file in system 7 prefernce folder and working with said file There are people looking for this code again, I posted it before. Since I posted it last, I have made some additions to it. This code is originally from Inside Mac Comm ToolBox. ============================================================================ FILE: doPrefs.proto.h ============================================================================ void getPrefsFile(Str255, OSType, OSType); Boolean getPrefsResourceStr(Str255, OSType, Str255); Boolean getPrefsResourceData(Str255, OSType, char *, long); void setPrefsResourceStr(Str255, OSType, Ptr, long); void setCTBpref(Str255); void DoCommPortSetup(Str255); ============================================================================ FILE: doPrefs.c ============================================================================ /****************************************************************************** * adapted from * Inside the Macintosh Communications Toolbox * Page 333 * After initialization, the code shown first checks if a * preferences folder, which contains tool settings written in preference * files, already exists. If so, the application uses the settings in this file. * Otherwise, the code generates a new preferences file. ****************************************************************************** * Modifications ****************************************************************************** * Nov 27, 1991 Original version had a bug in it. * If the "Preferences" directory already existed, the preferences file * was placed in the system folder, not in the "Preferences" directory. * If the directory did NOT exist, everything worked fine. * I also changed it to check for the existance of the "Preferences" folder * first instead of trying to create one and letting an duplicate file error * indicate that it already existed. * by Mark Sproul * AppleLink: Sproul.M * Jan 16, 1992 Working on a general purpose prefs file manipulator * Jan 25, 1992 Prefs file resources working great ******************************************************************************/ #include #include #include "doPrefs.proto.h" /* ** Global Variables used by all of the prefs routines */ long prefDirID; /* Prefs Dir ID number */ long sysfDirID; /* System folder Dir ID number */ short prefVRefNum; /* Prefs Volume Ref number */ short prefFileOKFlag = 0; /* prefs file OK indicator */ /************************************************************* * This makes sure there is a prefs file in the PREFERENCES directory * if not, it creates it. * * getPrefsFile MUST be called before any atempt at getting the prefs values * *************************************************************/ void getPrefsFile(prefsFileName, creatorType, fileType) Str255 prefsFileName; OSType creatorType; OSType fileType; { OSErr osErr = noErr; SysEnvRec theWorld; CInfoPBPtr infoPB; WDPBPtr wdPB; HParmBlkPtr dirPB; short prefRefNum; Point where = { 75, 75 }; Str63 toolName; short procID; Handle h; Ptr p; ConnHandle prefConn; ConnHandle docConn; CMBufferSizes sizes = { 0, 0, 0, 0, 0, 0, 0, 0 }; infoPB = (CInfoPBPtr)NewPtrClear(sizeof(*infoPB)); wdPB = (WDPBPtr)NewPtrClear(sizeof(*wdPB)); dirPB = (HParmBlkPtr)NewPtrClear(sizeof(*dirPB)); /* find the system folder's volume reference number and directory ID */ osErr = SysEnvirons(curSysEnvVers, &theWorld); (*wdPB).ioVRefNum = theWorld.sysVRefNum; if (noErr == (osErr = PBGetWDInfo(wdPB, false))) { /********************************************************* * 11-27-91 Modified by Mark Sproul **********************************************************/ /* get the Volume Reference Number and save it */ prefVRefNum = (*wdPB).ioWDVRefNum; /* get the System directory ID and save it */ sysfDirID = (*wdPB).ioWDDirID; /* check for the preferences folder */ (*infoPB).hFileInfo.ioFDirIndex = 0; (*infoPB).hFileInfo.ioVRefNum = prefVRefNum; (*infoPB).hFileInfo.ioDirID = sysfDirID; (*infoPB).hFileInfo.ioNamePtr = "\pPreferences"; osErr = PBGetCatInfo(infoPB, false); /* save the "Preferecnces" dir number */ prefDirID = (*infoPB).hFileInfo.ioDirID; if (osErr == fnfErr) { /* Create "Preferences" folder */ (*dirPB).fileParam.ioVRefNum = prefVRefNum; (*dirPB).fileParam.ioDirID = sysfDirID; (*dirPB).fileParam.ioNamePtr = "\pPreferences"; osErr = PBDirCreate(dirPB, false); prefDirID = (*dirPB).fileParam.ioDirID; } /********************************************************* * end of modifications **********************************************************/ if (osErr == noErr) { /* does the preference file exist? */ (*infoPB).hFileInfo.ioFDirIndex = 0; (*infoPB).hFileInfo.ioVRefNum = prefVRefNum; (*infoPB).hFileInfo.ioDirID = prefDirID; (*infoPB).hFileInfo.ioNamePtr = prefsFileName; osErr = PBGetCatInfo(infoPB, false); if (osErr == noErr) { /* set flag saying the prefs file is OK */ prefFileOKFlag = 0xAA; } if (osErr == fnfErr) { /* no, so create a new preference file */ if (noErr == (osErr = HCreate(prefVRefNum, prefDirID, prefsFileName, creatorType, fileType))) { HCreateResFile(prefVRefNum, prefDirID, prefsFileName); if (noErr == (osErr = ResError())) { /* open the preference file */ prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm); if (prefRefNum == -1) { osErr = ResError(); } else { /* create a default connection */ osErr = CRMGetIndToolName(classCM, 1,toolName); if (noErr == osErr) { prefConn = CMNew(CMGetProcID(toolName), cmData, sizes, 0, 0); /* leave the default setting as the preferance */ /************************************************************ * ORIGINALLY, the code let the user select setup at this point * I do not want it asking for serial port prefs on startup * if they are not set. * ---allow the user to select a prefered tool and configuration * ---osErr = CMChoose(&prefConn, where, nil); ************************************************************/ /* write the prefered tool name to the preference file */ HLock((Handle) prefConn); CMGetToolName((**prefConn).procID, toolName); HUnlock((Handle) prefConn); h = NewHandle(1 + toolName[0]); HLock(h); BlockMove(toolName, *h, GetHandleSize(h)); HUnlock(h); AddResource(h, 'pTXT', 0, ""); ReleaseResource(h); /* write the prefered configuration to the preference file */ p = CMGetConfig(prefConn); h = NewHandle(GetPtrSize(p)); HLock(h); BlockMove(p, *h, GetHandleSize(h)); HUnlock(h); AddResource(h, 'cTXT', 0, ""); ReleaseResource(h); DisposPtr(p); /* dispose of the connection */ CMDispose(prefConn); } /* close the file so that it can be used in a shared environment */ CloseResFile(prefRefNum); /* set flag saying the prefs file is OK */ prefFileOKFlag = 0xAA; } } } } } } } //************************************** //* get resource String from Prefs file //* //*************************************** Boolean getPrefsResourceStr(prefsFileName, perfsResType, returnString) Str255 prefsFileName; OSType perfsResType; Str255 returnString; { short prefRefNum; Handle h; int i; Size hSize; Boolean returnFlag; h = nil; returnFlag = false; /* did the prefs file get opened or created OK */ if (prefFileOKFlag == 0xAA) { /* focus on the preference file */ prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm); if (prefRefNum != -1) { h = Get1Resource(perfsResType, 0); if (h != nil) { hSize = GetHandleSize(h); /* get the size of the handle */ if (hSize > 255) hSize = 255; HLock(h); /* had to have this to make it compile under THINK C 5.0 */ for (i=0; i< hSize; i++) { returnString[i] = *(*h+i); } HUnlock(h); ReleaseResource(h); returnFlag = true; //* prefs string is OK } CloseResFile(prefRefNum); } } else { //* generate prefs file alert message } return(returnFlag); } //************************************** //* get resource String from Prefs file //* //*************************************** Boolean getPrefsResourceData( Str255 prefsFileName, OSType perfsResType, char *returnString, long maxLen) { short prefRefNum; Handle h; int i; Size hSize; Boolean returnFlag; h = nil; returnFlag = false; /* did the prefs file get opened or created OK */ if (prefFileOKFlag == 0xAA) { /* focus on the preference file */ prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm); if (prefRefNum != -1) { h = Get1Resource(perfsResType, 0); if (h != nil) { hSize = GetHandleSize(h); /* get the size of the handle */ if (hSize > maxLen) hSize = maxLen; HLock(h); /* had to have this to make it compile under THINK C 5.0 */ for (i=0; i< hSize; i++) { returnString[i] = *(*h+i); } HUnlock(h); ReleaseResource(h); returnFlag = true; //* prefs string is OK } CloseResFile(prefRefNum); } } else { //* generate prefs file alert message } return(returnFlag); } //************************************** //* save resource to Prefs file //* //*************************************** void setPrefsResourceStr(Str255 prefsFileName, OSType perfsResType,char *buffer, long bufLen) { short prefRefNum; Handle h; int i; Size hSize, newSize; h = nil; /* did the prefs file get opened or created OK */ if (prefFileOKFlag == 0xAA) { /* focus on the preference file */ prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm); if (prefRefNum != -1) { //* get the specified resource */ h = Get1Resource(perfsResType, 0); if (h == nil) { /* resource did not exist, add it to resource file */ h = NewHandle(1 + bufLen); HLock(h); BlockMove(buffer, *h, GetHandleSize(h)); HUnlock(h); AddResource(h, perfsResType, 0, "\p"); } else { /* resoure DOES exist, change it */ hSize = GetHandleSize(h); /* get the size of the handle */ /* check for size of handle */ if (hSize != (1 + bufLen)) { newSize = 1 + bufLen; SetHandleSize(h, newSize); } HLock(h); BlockMove(buffer, *h, GetHandleSize(h)); HUnlock(h); ChangedResource(h); } ReleaseResource(h); CloseResFile(prefRefNum); } } else { PutAlertMessage("\pProgram cant find prefs file"); } } /************************************** * set Comm Tool Box connection preference * ****************************************/ void setCTBpref(prefsFileName) Str255 prefsFileName; { short prefRefNum; OSErr osErr, cmChooseReturnCode; int i; Str255 prefStr; short procID; Str63 toolName; Handle h; Ptr p; short iErr; Size hSize, newSize; ConnHandle docConn; CMBufferSizes sizes = { 0, 0, 0, 0, 0, 0, 0, 0 }; Point where = { 40, 40 }; if (isCTBavailable()) { /* did the prefs file get opened or created OK */ if (prefFileOKFlag == 0xAA) { getPrefsResourceStr(prefsFileName,'pTXT', prefStr); procID = CMGetProcID(prefStr); if (procID != -1) { /* create a new connection */ docConn = CMNew(procID, cmData, sizes, 0, 0); if (docConn != nil) { /* set the prefered configuration */ getPrefsResourceStr(prefsFileName,'cTXT', prefStr); osErr = CMSetConfig(docConn, (char *)prefStr); cmChooseReturnCode = CMChoose(&docConn, where, nil); } } else { /* the prefered tool could not be found so I */ osErr = CRMGetIndToolName(classCM, 1, toolName); docConn = CMNew(CMGetProcID(toolName), cmData, sizes, 0, 0); if (docConn != nil) { cmChooseReturnCode = CMChoose(&docConn, where, nil); } } if ((cmChooseReturnCode == chooseOKMinor) || (cmChooseReturnCode == chooseOKMajor)) { /* change the prefs file */ if (prefFileOKFlag == 0xAA) { /* open the preference file */ prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm); if (prefRefNum != -1) { /* write the prefered tool name to the preference file */ HLock((Handle) docConn); CMGetToolName((**docConn).procID, toolName); HUnlock((Handle) docConn); /* get the port TeXT resource */ h = Get1Resource('pTXT', 0); if (h == nil) { /* resource did not exist, add it to resource file */ h = NewHandle(1 + toolName[0]); HLock(h); BlockMove(toolName, *h, GetHandleSize(h)); HUnlock(h); AddResource(h, 'pTXT', 0, ""); } else { /* resoure DOES exist, change it */ hSize = GetHandleSize(h); /* get the size of the handle */ /* check for size of handle */ if (hSize != (1 + toolName[0])) { newSize = 1 + toolName[0]; SetHandleSize(h, newSize); } HLock(h); BlockMove(toolName, *h, GetHandleSize(h)); HUnlock(h); ChangedResource(h); } ReleaseResource(h); /* write the prefered configuration to the preference file */ p = CMGetConfig(docConn); /* get the configuration TeXT resource */ h = Get1Resource('cTXT', 0); if (h == nil) { /* resource did not exist, add it to resource file */ h = NewHandle(GetPtrSize(p)); HLock(h); BlockMove(p, *h, GetHandleSize(h)); HUnlock(h); AddResource(h, 'cTXT', 0, ""); iErr = ResError(); } else { /* resoure DOES exist, change it */ hSize = GetHandleSize(h); /* get the size of the handle */ /* check for size of handle */ if (hSize != GetPtrSize(p)) { newSize = GetPtrSize(p); SetHandleSize(h, newSize); } HLock(h); BlockMove(p, *h, GetHandleSize(h)); HUnlock(h); ChangedResource(h); } ReleaseResource(h); CloseResFile(prefRefNum); iErr = ResError(); DisposPtr(p); } } } if (docConn != nil) { /* dispose of the connection */ CMDispose(docConn); } } } else { SysBeep(1); SysBeep(1); } } void DoCommPortSetup(prefsFileName) Str255 prefsFileName; { setCTBpref(prefsFileName); } - ----------------------------------------------------- Mark Sproul - KB2ICI - New Jersey sproul@sproul.com +++++++++++++++++++++++++++ From: grobbins@Apple.COM (Grobbins) Date: 7 Jan 93 09:42:05 GMT Organization: Mac Experimental System Software In article Sproul@sproul.sproul.com (Mark Sproul) writes: >Below is code for finding and working with prefs file in system 7 >prefernce folder and working with said file Modern Mac applications should use the FindFolder call to locate the Preferences folder. For an example, see the DTS snippet prefs (available via anonymous ftp from ftp.apple.com, in the directory /dts/mac/sc/snippets/toolbox.) FindFolder is documented in the Finder Interface chapter of Inside Mac VI. Grobbins grobbins@apple.com No disclaimers apply. +++++++++++++++++++++++++++ From: neeri@iis.ethz.ch (Matthias Neeracher) Date: 7 Jan 93 22:41:43 GMT Organization: Integrated Systems Laboratory, ETH, Zurich In article , Sproul@sproul.sproul.com (Mark Sproul) writes: > Below is code for finding and working with prefs file in system 7 > prefernce folder and working with said file Why is this code better than FindFolder() ? > /* check for the preferences folder */ > (*infoPB).hFileInfo.ioFDirIndex = 0; > (*infoPB).hFileInfo.ioVRefNum = prefVRefNum; > (*infoPB).hFileInfo.ioDirID = sysfDirID; > (*infoPB).hFileInfo.ioNamePtr = "\pPreferences"; >From SysTypes.r: #define Language langEnglish, langFrench, langGerman, langItalian, langDutch, langSwedish, langSpanish, langDanish, \ langPortuguese, langNorwegian, langHebrew, \ langJapanese, langArabic, langFinnish, langGreek, \ langIcelandic, langMaltese, langTurkish, \ langCroatian, langTradChinese, langUrdu, \ langHindi, langThai, langKorean, langLithuanian, \ langPolish, langHungarian, langEstonian, \ langLettish, langLappish, langFaeroese, \ langFarsi, langRussian, langSimpChinese, \ langFlemish, langIrish, langAlbanian, langRomanian, \ langCzech, langSlovak, langSlovenian, langYiddish, \ langSerbian, langMacedonian, langBulgarian, \ langUkrainian, langByelorussian, langUzbek, \ langKazakh, langAzerbaijani, langAzerbaijanAr, \ langArmenian, langGeorgian, langMoldavian, \ langKirghiz, langTajiki, langTurkmen, \ langMongolian, langMongolianCyr, langPashto, \ langKurdish, langKashmiri, langSindhi, langTibetan, \ langNepali, langSanskrit, langMarathi, langBengali, \ langAssamese, langGujarati, langPunjabi, langOriya, \ langMalayalam, langKannada, langTamil, langTelugu, \ langSinhalese, langBurmese, langKhmer, langLao, \ langVietnamese, langIndonesian, langTagalog, \ langMalayRoman, langMalayArabic, langAmharic, \ langTigrinya, langGalla, langSomali, langSwahili, \ langRuanda, langRundi, langChewa, langMalagasy, \ langEsperanto, langUnspecified = 32767 \ Are you sure that the speakers of all of these languages will be happy with your hardcoding the name "Preferences" ? Matthias - ----- Matthias Neeracher neeri@iis.ethz.ch "There once was an Age of Reason, but we've progressed beyond it." -- Ayn Rand, _Atlas Shrugged_ +++++++++++++++++++++++++++ From: jesjones@stein.u.washington.edu (Jesse Jones) Date: 9 Jan 1993 05:28:21 GMT Organization: University of Washington, Seattle There has been some discussion lately about how to handle a preferences file, so I thought I'd post my code. It's nothing astounding, but I think it has some advantages over the previously posted code: 1) FindFolder and the high level file manager routines are used so the code is quit a bit easier to follow. 2) The GetPref procedure is passed a procedure parameter that is used to return a default preference if the preference is missing from the Prefs file. 3) Individual preferences are referenced by the ResType, resource name, and version number. If the version numbers don't match the default preference is returned. --Jesse Here is the interface for the Prefs module. It's written in SemperSoft Modula-2. HANDLE is compatible with any pointer to pointer and Mstring is a 256-byte string terminated with zero. DEFINITION MODULE Prefs; (* By Jesse Jones, 1992 *) FROM SYSTEM IMPORT HANDLE; FROM Types IMPORT Str255, Mstring; TYPE PrefsProc = PROCEDURE(): HANDLE; VAR PrefsName: Str255; (* Name of the Preferences file. *) PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE; (* default is called if Prefs not present or version <> vers. *) PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE); (* Change or add a preference. *) PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN; PROCEDURE DeletePref (rType, rName: Mstring); END Prefs. And here is the implementation of the Prefs module. IMPLEMENTATION MODULE Prefs; (* Import list deleted *) (* Version numbers are saved in PVRS resources. These resources contain a single integer for the version number and are referenced by name, with the name being the concatenation of the preference resource type and the preference's name. *) PROCEDURE GetVersion (rType, rName: Mstring): INTEGER; VAR data: IntHandle; str : Str255; vers: INTEGER; err : OSErr; BEGIN str := StrToPStr(Concat(rType, rName)); data := Get1NamedResource('PVRS', str); err := ResError(); IF (err = resNotFound) OR (data = NIL) THEN vers := -1; ELSIF ErrCheck(err) THEN vers := data^^; ELSE vers := -1; END; RETURN vers; END GetVersion; PROCEDURE SetVersion (rType, rName: Mstring; version: INTEGER); VAR data : IntHandle; str : Mstring; err : OSErr; dummy: BOOLEAN; BEGIN str := Concat(rType, rName); data := Get1NamedResource('PVRS', StrToPStr(str)); err := ResError(); IF (err = resNotFound) OR (data = NIL) THEN data := CreateHandle(SIZE(INTEGER)); data^^ := version; dummy := InsertResource('PVRS', str, 0, data, TRUE); ELSIF ErrCheck(err) THEN data^^ := version; dummy := ChangeResource(data); END; END SetVersion; PROCEDURE OpenPrefs (VAR refNum: INTEGER; perm: SignedByte): BOOLEAN; VAR vRef : INTEGER; dir : LONGINT; err : OSErr; dummy: BOOLEAN; BEGIN err := FindPrefsFolder(vRef, dir); (* find (or create) prefs folder *) IF ErrCheck(err) THEN refNum := HOpenResFile(vRef, dir, PrefsName, perm); (* open prefs file *) err := ResError(); IF err = fnfErr THEN (* if prefs file is missing *) HCreateResFile(vRef, dir, PrefsName); (* then create it *) refNum := HOpenResFile(vRef, dir, PrefsName, perm); (* and open the new file *) err := ResError(); dummy := ErrCheck(err); END; END; RETURN err = noErr; END OpenPrefs; PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE; VAR refNum: INTEGER; data : HANDLE; err : OSErr; BEGIN IF OpenPrefs(refNum, fsRdPerm) THEN IF GetVersion(rType, rName) <> vers THEN data := default(); ELSE data := Get1NamedResource(StrToOS(rType), StrToPStr(rName)); err := ResError(); IF (err = resNotFound) OR (data = NIL) THEN data := default(); ELSIF ErrCheck(err) THEN DetachResource(data); HNoPurge(data); ELSE data := default(); END; END; CloseResFile(refNum); ELSE data := default(); END; Assert(data <> NIL); RETURN data; END GetPref; PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN; VAR refNum: INTEGER; data : HANDLE; found : BOOLEAN; err : OSErr; BEGIN found := FALSE; IF OpenPrefs(refNum, fsRdPerm) THEN IF GetVersion(rType, rName) >= vers THEN data := Get1NamedResource(StrToOS(rType), StrToPStr(rName)); err := ResError(); IF (err <> resNotFound) AND (data <> NIL) THEN found := ErrCheck(err); END; END; CloseResFile(refNum); END; RETURN found; END PrefPresent; (* SetPref is the only routine to actually add a preference to the Prefs file. Note that the preferences are purgable. *) PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE); VAR refNum: INTEGER; old : HANDLE; err : OSErr; done : BOOLEAN; BEGIN done := FALSE; Assert(data <> NIL); IF OpenPrefs(refNum, fsRdWrPerm) THEN old := Get1NamedResource(StrToOS(rType), StrToPStr(rName)); err := ResError(); IF (err = resNotFound) OR (old = NIL) THEN done := InsertResource(rType, rName, 0, data, TRUE); ELSIF ErrCheck(err) THEN RemoveResource(old); done := InsertResource(rType, rName, 0, data, TRUE); END; IF done THEN SetVersion(rType, rName, vers); DetachResource(data); END; CloseResFile(refNum); END; END SetPref; PROCEDURE DeletePref (rType, rName: Mstring); VAR refNum: INTEGER; old : HANDLE; err : OSErr; BEGIN IF OpenPrefs(refNum, fsRdWrPerm) THEN old := Get1NamedResource(StrToOS(rType), StrToPStr(rName)); err := ResError(); IF (err = resNotFound) OR (old = NIL) THEN (* do nothing *) ELSIF ErrCheck(err) THEN RemoveResource(old); END; CloseResFile(refNum); END; END DeletePref; BEGIN PrefsName := ""; END Prefs. InsertResource, ChangeResource, and RemoveResource are some handy resource manipulation routines I wrote a while back. I'll post them in a seperate message. +++++++++++++++++++++++++++ From: jesjones@stein.u.washington.edu (Jesse Jones) Date: 10 Jan 93 01:55:19 GMT Organization: University of Washington, Seattle Here are some handy routines for dealing with resources. Several of these routines are used by the Prefs code I posted recently. A few of these routines replace Apple's standard routines with something that behaves a little better. --Jesse PROCEDURE Unique (theType: ResType): INTEGER; INLINE2(0A9C1H); (* The toolbox trap, UniqueID, returns an unused positive resource ID number. However, application resources need to be larger than 127 to avoid conflicts with other resources used by the System. The below procedure ensures that the ID is in the correct range. *) PROCEDURE UniqueID (theType: ResType; system: BOOLEAN): INTEGER; VAR ID : INTEGER; done: BOOLEAN; BEGIN REPEAT ID := Unique(theType); (* always returns positive IDs *) IF system THEN done := ID < 128; ELSE done := ID > 127; END; UNTIL done; RETURN ID; END UniqueID; PROCEDURE Unique1 (theType: ResType): INTEGER; INLINE2(0A810H); PROCEDURE Unique1ID (theType: ResType; system: BOOLEAN): INTEGER; VAR ID : INTEGER; done: BOOLEAN; BEGIN REPEAT ID := Unique1(theType); (* always returns positive IDs *) IF system THEN done := ID < 128; ELSE done := ID > 127; END; UNTIL done; RETURN ID; END Unique1ID; PROCEDURE GetResourceName (rsrc: HANDLE): Mstring; VAR rID : INTEGER; kind : ResType; rName: Str255; BEGIN GetResInfo(rsrc, rID, kind, rName); RETURN PStrToStr(rName); END GetResourceName; PROCEDURE GetResourceID (rsrc: HANDLE): INTEGER; VAR rID : INTEGER; kind : ResType; rName: Str255; BEGIN GetResInfo(rsrc, rID, kind, rName); RETURN rID; END GetResourceID; (* resources can be specified using either name, or ID (if name is ""). *) PROCEDURE InsertResource (type, name: Mstring; ID: INTEGER; rsrc: HANDLE; purgeable: BOOLEAN): BOOLEAN; VAR rName : Str255; rType : ResType; refNum: INTEGER; done : BOOLEAN; BEGIN done := FALSE; rType := StrToOS(type); IF name[0] = EOS THEN rName := ""; ELSE rName := StrToPStr(name); ID := Unique1ID(rType, FALSE); END; AddResource(rsrc, rType, ID, rName); IF ErrCheck(ResError()) THEN IF purgeable THEN SetResAttrs(rsrc, resPurgeable) END; (* clears resChanged bit! *) IF ErrCheck(ResError()) THEN ChangedResource(rsrc); IF ErrCheck(ResError()) THEN refNum := CurResFile(); UpdateResFile(refNum); done := ErrCheck(ResError()); END; END; END; RETURN done; END InsertResource; PROCEDURE ChangeResource (rsrc: HANDLE): BOOLEAN; VAR refNum: INTEGER; done : BOOLEAN; BEGIN done := FALSE; ChangedResource(rsrc); IF ErrCheck(ResError()) THEN refNum := CurResFile(); UpdateResFile(refNum); done := ErrCheck(ResError()); END; RETURN done; END ChangeResource; PROCEDURE RemoveResource (rsrc: HANDLE); VAR refNum: INTEGER; done : BOOLEAN; BEGIN RmveResource(rsrc); IF ErrCheck(ResError()) THEN refNum := CurResFile(); UpdateResFile(refNum); KillHandle(rsrc); done := ErrCheck(ResError()); END; END RemoveResource; --------------------------- End of C.S.M.P. Digest **********************